package io.summer.gadget.channel.core;

import io.summer.basis.tool.IpTool;
import io.summer.gadget.RedisFacade;
import io.summer.redis.tool.RedisTool;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 使用redis进行简单的服务注册
 *
 * @author ml
 */
@Slf4j
public class ServiceRegistry extends AbstractServiceHelper implements ApplicationRunner {
  private static final String SCHEME = "http";
  private static final String AUTO_ADDRESS = "auto";

  private final ServiceCheck serviceCheck;

  private final Pattern HAS_PORT = Pattern.compile(".*:(-?\\d+)$");

  @Value("${summer.gadget.channel.self-address:}")
  @Getter
  private String serviceAddress;

  @Value("${spring.application.name}")
  @Getter
  private String appName;

  @Value("${server.port:8080}")
  private String port;

  @Autowired
  private RedisTool redisTool;

  @Autowired
  private RedisFacade redisFacade;

  public ServiceRegistry(ServiceCheck serviceCheck) {
    this.serviceCheck = serviceCheck;
  }

  private boolean ensureServiceAddress() {
    if (StringUtils.isEmpty(serviceAddress)
      || serviceAddress.compareToIgnoreCase(AUTO_ADDRESS) == 0) {
      serviceAddress = String.format("%s://%s:%s", SCHEME, IpTool.getLocalhostIp(), port);
    }
    if (!serviceAddress.startsWith(SCHEME)) {
      serviceAddress = String.format("%s://%s", SCHEME, serviceAddress);
    }
    Matcher matcher = HAS_PORT.matcher(serviceAddress);
    if (!matcher.matches()) {
      serviceAddress += ":80";
    }

    int port = parseUrl(serviceAddress).getPort();
    return IpTool.checkPort(port);
  }

  public List<String> getServers(String appName) {
    return RedisFacade.hashFields(getServiceKey(appName));
  }

  public void subscribe(String topic) {
    if (StringUtils.isEmpty(topic)) {
      log.warn("subscribe on null topic");
      return;
    }
    redisTool.hash().put(getTopicKey(topic), appName, "1");
  }

  public void unSubscribe(String topic) {
    if (StringUtils.isEmpty(topic)) {
      log.warn("unSubscribe on null topic");
      return;
    }
    redisTool.hash().delete(getTopicKey(topic), appName);
  }

  private void registerSelf() {
    if (ensureServiceAddress()) {
      log.info("register server: {}@{}", appName, serviceAddress);
      registerServer(appName, serviceAddress);
    } else {
      log.warn("register server, invalid port: {}", port);
    }
  }

  @PreDestroy
  public void unregister() {
    log.info("unregister server: {}@{}", appName, serviceAddress);
    unregisterServer(appName, serviceAddress);
  }

  @Override
  public void run(ApplicationArguments args) throws Exception {
    registerSelf();
    serviceCheck.startAliveCheck();
  }
}
